package parserXMI;

import java.util.ArrayList;
import java.util.List;

import codigoObjeto.Assembly;
import codigoObjeto.Codigo;
import codigoObjeto.Operator;
import codigoObjeto.Registers;
import model.*;

public class Method implements Typable, Codable{
	private String name;
	private Visibility visibility;
	private Type type;
	private List<Variable> parameters = new ArrayList<Variable>();
	private List<Variable> variables = new ArrayList<Variable>();
	private Label next;
	private String code;
	private String addr;
	
	public Method(String name, Visibility visibility, Type returnType){
		this.name = name;
		this.visibility = visibility;
		this.type = returnType;
	}
	
	public Method(String name, Type returnType){
		this.name = name;
		this.type = returnType;
	}
	
	public Method(String name){
		this(name, null, null);
	}
	
	public Method(String name, String type){
		this(name, null, Types.getType(type));
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Visibility getVisibility() {
		return visibility;
	}

	public void setVisibility(Visibility visibility) {
		this.visibility = visibility;
	}
	
	public void setVisibility(String visibilidade) {
		this.visibility = Visibility.contains(visibilidade);
	}

	public Type getType() {
		return type;
	}

	public void setType(Type returnType) {
		this.type = returnType;
	}

	public void setReturnType(String returnType) {
		this.type = Types.getType(returnType);
	}
	public List<Variable> getParameters() {
		return parameters;
	}

	public void setParameters(List<Variable> parameters) {
		this.parameters = parameters;
	}

	public List<Variable> getVariables() {
		return variables;
	}

	public void setVariables(List<Variable> variables) {
		this.variables = variables;
	}
	
	public String toString(){
		String str =  getName()+"(";
		for (Variable variable : this.parameters) {
			str += variable.toString() + ", ";
		}
		if(this.parameters.size() != 0) str = str.substring(0, str.length()-2);
		str += ") : " + type;
		return str;
	}

	@Override
	public void gen3Address() {
		String result = "";
		setAddr(Temp.getTemp().getName());
		for (Variable variable : parameters) {
			result += Printer.gen("param " + variable.getName());
		}
		String code = getAddr() + " = call " + getName();
		if(getParameters().size() > 0)
			code+=", " + getParameters().size();
		result += Printer.gen(code);
		setCode(result);
	}
	
	@Override
	public String genObjectCode() {
		
		//Rotulo	ADD	SP	SP  #mize
		Codigo initMethod = new Codigo();
		initMethod.setOperator(Operator.ADD);
		initMethod.setDestino("SP");
		
		//Rotulo	ST	SP  subRotulo
		Codigo st = new Codigo();
		st.setOperator(Operator.ST);
		st.setDestino("SP");
		
		//Rotulo	BR	enderecoMetodo
		Codigo branch = new Codigo();
		branch.setOperator(Operator.BR);
		branch.setOperando(Registers.getMethodPosition()+"");
		
		//Rotulo	SUB	SP	SP  #mize
		Codigo sub = new Codigo();
		sub.setOperator(Operator.SUB);
		sub.setDestino("SP");
		
		st.setOperando("SP, " + sub.getRotulo().getLabel());

		Codigo halt = null;
		Integer methodSize = 0;
		if (getType() != null){
			//Metodo com retorno, add "HALT"
			//Rotulo HALT
			halt = new Codigo();
			halt.setOperator(Operator.HALT);
			
			methodSize = Integer.parseInt(halt.getRotulo().getLabel()) - Integer.parseInt(initMethod.getRotulo().getLabel());
		} else {
			methodSize = Integer.parseInt(sub.getRotulo().getLabel()) - Integer.parseInt(initMethod.getRotulo().getLabel());	
		}
		
		String methodSizePath = "SP, " + methodSize;
		
		initMethod.setOperando(methodSizePath);
		sub.setOperando(methodSizePath);
				
		Assembly.add(initMethod);
		Assembly.add(st);
		Assembly.add(branch);
		Assembly.add(sub);

		if (halt != null) Assembly.add(halt);
		
		return null;
	}

	@Override
	public String getAddr() {
		return this.addr;
	}

	@Override
	public void setAddr(String temp) {
		this.addr = temp;
		
	}

	@Override
	public Label getNext() {
		return next;
	}

	@Override
	public void setNext(Label next) {
		this.next = next;
		
	}

	@Override
	public String getCode() {
		if(code == null || code.equals(""))
			gen3Address();
		return code;
	}

	@Override
	public void setCode(String code) {
		this.code = code;
		
	}
	
	public boolean addParameter(Variable va){
		for (Variable variable : getParameters()) {
			if(variable.getName().equals(va.getName()))
				return false;
		}
		return getParameters().add(va);
	}
	
	public boolean addAllParameters(List<Variable> vas){
		for (Variable variable : vas) {
			addParameter(variable);
		}
		return true;
	}
		
	
}
